package com.jhsw.roadsun.module.business.service.config;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.jhsw.roadsun.framework.common.pojo.PageResult;
import com.jhsw.roadsun.framework.common.util.object.BeanUtils;
import com.jhsw.roadsun.module.business.controller.admin.config.vo.BizConfigPageReqVO;
import com.jhsw.roadsun.module.business.controller.admin.config.vo.BizConfigSaveReqVO;
import com.jhsw.roadsun.module.business.controller.app.config.vo.AppBasicConfigRespVO;
import com.jhsw.roadsun.module.business.dal.dataobject.config.BizConfigDO;
import com.jhsw.roadsun.module.business.dal.mysql.config.BizConfigMapper;
import com.jhsw.roadsun.module.business.enums.config.ConfigEnum;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import static com.jhsw.roadsun.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.jhsw.roadsun.module.business.enums.ErrorCodeConstants.BIZ_CONFIG_NOT_EXISTS;
import static com.jhsw.roadsun.module.business.enums.ErrorCodeConstants.BIZ_CONFIG_ORDER_NOT_LESS_NEXT_ORDER_NO;
import static com.jhsw.roadsun.module.business.enums.config.ConfigEnum.*;

/**
 * 参数配置 Service 实现类
 *
 * @author Ysugar
 */
@Service
@Validated
public class BizConfigServiceImpl implements BizConfigService {

    @Resource
    private BizConfigMapper bizConfigMapper;

    private static final ReentrantLock ORDER_NO_LOCK = new ReentrantLock();

    @Override
    public Long createBizConfig(BizConfigSaveReqVO createReqVO) {
        // 插入
        BizConfigDO bizConfig = BeanUtils.toBean(createReqVO, BizConfigDO.class);
        bizConfigMapper.insert(bizConfig);

        // 返回
        return bizConfig.getId();
    }

    @Override
    public void updateBizConfig(BizConfigSaveReqVO updateReqVO) {
        // 校验存在
        validateBizConfigExists(updateReqVO.getId());

        // 判断id == 5，则判断用户新输入的订单号，是否 < 下一个订单号
        if (5 == updateReqVO.getId()){
            BizConfigDO bizConfigDO = bizConfigMapper.selectById(6);
            BigDecimal nextOrderNo = new BigDecimal(bizConfigDO.getValue());
            if (new BigDecimal(updateReqVO.getValue()).compareTo(nextOrderNo) < 0)
                throw exception(BIZ_CONFIG_ORDER_NOT_LESS_NEXT_ORDER_NO, nextOrderNo);

            // 反之不小于，则设置下一个订单号为新输入的订单号
            updateNextOrderNo(updateReqVO.getValue());
        }

        // 更新
        BizConfigDO updateObj = BeanUtils.toBean(updateReqVO, BizConfigDO.class);
        bizConfigMapper.updateById(updateObj);
    }

    @Override
    public void deleteBizConfig(Long id) {
        // 校验存在
        validateBizConfigExists(id);
        // 删除
        bizConfigMapper.deleteById(id);
    }

    @Override
        public void deleteBizConfigListByIds(List<Long> ids) {
        // 删除
        bizConfigMapper.deleteByIds(ids);
        }


    private void validateBizConfigExists(Long id) {
        if (bizConfigMapper.selectById(id) == null) {
            throw exception(BIZ_CONFIG_NOT_EXISTS);
        }
    }

    @Override
    public BizConfigDO getBizConfig(Long id) {
        return bizConfigMapper.selectById(id);
    }

    @Override
    public PageResult<BizConfigDO> getBizConfigPage(BizConfigPageReqVO pageReqVO) {
        return bizConfigMapper.selectPage(pageReqVO);
    }

    @Override
    public String updateNextOrderNo(String newValue) {
        try {
            ORDER_NO_LOCK.lock();
            String oldOrderNo = getBizConfigByKey(NEXT_ORDER_NO, String.class);

            if (null == newValue){
                int length = oldOrderNo.length();

                // 转换订单号，递增 1，并根据位数补 0
                newValue = String.format("%0" + length + "d", Long.parseLong(oldOrderNo) + 1);
            }

            bizConfigMapper.update(BizConfigDO::getConfigKey, NEXT_ORDER_NO.getValue(), BizConfigDO::getValue, newValue);

            return oldOrderNo;
        } finally {
            if (ORDER_NO_LOCK.isLocked()) {
                ORDER_NO_LOCK.unlock();
            }
        }
    }

    @Override
    public <T> T getBizConfigByKey(ConfigEnum configEnum, Class<T> cls) {
        BizConfigDO config = bizConfigMapper.selectOne(BizConfigDO::getConfigKey, configEnum.getValue());
        if (Objects.isNull(config))
            throw exception(BIZ_CONFIG_NOT_EXISTS);

        return cn.hutool.core.convert.Convert.convert(cls, config.getValue());
    }

    @Override
    public <K, T> Map<K, T> getBizConfigMapByKeys(Class<K> kCls, Class<T> cls, ConfigEnum... configEnums) {
        List<BizConfigDO> bizConfigs = bizConfigMapper.selectList(Wrappers.<BizConfigDO>lambdaQuery()
                .in(BizConfigDO::getConfigKey, Arrays.stream(configEnums).map(ConfigEnum::getValue).collect(Collectors.toList())));
        return bizConfigs
                .stream()
                .collect(Collectors
                        .groupingBy(o -> kCls.equals(ConfigEnum.class) ? kCls.cast(ConfigEnum.find(o.getConfigKey())) : kCls.cast(o.getConfigKey()),
                                Collectors.mapping(BizConfigDO::getValue,
                                        Collectors.collectingAndThen(Collectors.toList(),
                                                list -> list.isEmpty() ? null : cn.hutool.core.convert.Convert.convert(cls, list.get(0))))));
    }

    @Override
    public AppBasicConfigRespVO getBizConfigBasic() {
        Map<ConfigEnum, String> configMap = this.getBizConfigMapByKeys(ConfigEnum.class, String.class, PROJECT_NAME,
                PROJECT_LOGO,
                USER_AGREEMENT,
                PRIVACY_POLICY);

        AppBasicConfigRespVO respVO = new AppBasicConfigRespVO();

        respVO.setName(configMap.get(PROJECT_NAME));
        respVO.setLogo(configMap.get(PROJECT_LOGO));
        respVO.setUserAgreement(configMap.get(USER_AGREEMENT));
        respVO.setPrivacyPolicy(configMap.get(PRIVACY_POLICY));

        return respVO;
    }
}